package cn.alex.sso.project.service.impl;

import cn.alex.sso.framework.core.PageHelper;
import cn.alex.sso.framework.enums.StatusEnum;
import cn.alex.sso.framework.global.ServiceException;
import cn.alex.sso.project.entity.SysDept;
import cn.alex.sso.project.entity.SysUser;
import cn.alex.sso.project.entity.params.EditSysUserPassword;
import cn.alex.sso.project.entity.params.EditSysUserStatus;
import cn.alex.sso.project.mapper.SysRoleClientMapper;
import cn.alex.sso.project.mapper.SysUserMapper;
import cn.alex.sso.project.mapper.SysUserRoleMapper;
import cn.alex.sso.project.service.SysUserDeptService;
import cn.alex.sso.project.service.SysUserRoleService;
import cn.alex.sso.project.service.SysUserService;
import cn.alex.sso.project.sync.event.UserAbstractDataSyncEvent;
import cn.alex.sso.project.sync.module.UserDataSyncModule;
import cn.dev33.satoken.secure.BCrypt;
import cn.dev33.satoken.sso.template.SaSsoUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * @author Alex
 * @description 针对表【sys_user(用户表)】的数据库操作Service实现
 * @createDate 2024-09-24 17:05:38
 */
@Service
@RequiredArgsConstructor
public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> implements SysUserService {
    private final SysUserRoleService userRoleService;
    private final SysUserDeptService userDeptService;
    private final SysUserRoleMapper userRoleMapper;
    private final SysRoleClientMapper roleClientMapper;

    @Override
    public IPage<SysUser> getPage(SysUser entity) {
        return baseMapper.selectPage(PageHelper.getPage(), entity);
    }

    @Override
    public SysUser getInfo(Long id) {
        return baseMapper.selectById(id);
    }

    @Override
    public void add(SysUser entity) {
        entity.setCreatedTime(new Date());
        entity.setUpdatedTime(new Date());
        entity.setPassword(BCrypt.hashpw(entity.getPassword()));

        SysUser userByUsername = getUserByUsername(entity.getUsername());
        if (ObjUtil.isNotEmpty(userByUsername)) {
            throw new ServiceException("当前账号已存在");
        }

        baseMapper.insert(entity);

        userRoleService.change(entity.getId(), entity.getRoleList());
        userDeptService.change(entity.getId(), new ArrayList<SysDept>() {{
            add(entity.getDept());
        }});

        SpringUtil.getApplicationContext().publishEvent(new UserAbstractDataSyncEvent(this, entity, UserDataSyncModule.Trigger.ADD));
    }

    @Override
    public void edit(SysUser entity) {
        entity.setUpdatedTime(new Date());
        baseMapper.updateById(entity);

        userRoleService.change(entity.getId(), entity.getRoleList());
        userDeptService.change(entity.getId(), new ArrayList<SysDept>() {{
            add(entity.getDept());
        }});

        SpringUtil.getApplicationContext().publishEvent(new UserAbstractDataSyncEvent(this, entity, UserDataSyncModule.Trigger.EDIT));
    }

    @Override
    public void del(List<Long> ids) {
        baseMapper.deleteByIds(ids);
        ids.forEach(id -> {
            userRoleService.change(id, null);
            userDeptService.change(id, null);
        });

        for (Long id : ids) {
            SpringUtil.getApplicationContext().publishEvent(new UserAbstractDataSyncEvent(this, id, UserDataSyncModule.Trigger.DELETE));
        }
    }

    @Override
    public SysUser getUserByUsername(String username) {
        return baseMapper.selectOne(new LambdaQueryWrapper<SysUser>()
                .eq(SysUser::getUsername, username)
        );
    }

    @Override
    public boolean existsUserInClient(Long userId, Long clientId) {
        List<Long> roleIdList = userRoleMapper.getRoleIdListByUserId(userId);
        List<Long> clientIdList = roleClientMapper.getClientIdListByRoleId(roleIdList);
        if (CollUtil.isEmpty(clientIdList)) {
            return false;
        }
        return clientIdList.contains(clientId);
    }

    @Override
    public void editStatus(EditSysUserStatus editSysUserStatus) {
        baseMapper.update(new LambdaUpdateWrapper<SysUser>()
                .eq(SysUser::getId, editSysUserStatus.getId())
                .set(SysUser::getStatus, editSysUserStatus.getStatus())
        );

        if (editSysUserStatus.getStatus().equals(StatusEnum.NO.getStatus())) {
            // 将当前用户退出登录
            SaSsoUtil.ssoLogout(editSysUserStatus.getId());
        }

        SpringUtil.getApplicationContext().publishEvent(new UserAbstractDataSyncEvent(this, editSysUserStatus, UserDataSyncModule.Trigger.EDIT_STATUS));
    }

    @Override
    public void editPassWord(EditSysUserPassword editSysUserPassword) {
        SysUser user = baseMapper.selectById(editSysUserPassword.getId());
        if (StrUtil.isNotBlank(editSysUserPassword.getOldPassword())) {
            if (!BCrypt.checkpw(editSysUserPassword.getOldPassword(), user.getPassword())) {
                throw new ServiceException("旧密码错误");
            }
        }

        baseMapper.update(new LambdaUpdateWrapper<SysUser>()
                .eq(SysUser::getId, editSysUserPassword.getId())
                .set(SysUser::getPasswordLevel, editSysUserPassword.getPasswordLevel())
                .set(SysUser::getPassword, BCrypt.hashpw(editSysUserPassword.getNewPassword()))
        );

        // 将当前用户退出登录
        SaSsoUtil.ssoLogout(editSysUserPassword.getId());
    }

    @Async
    @Override
    public void updateLastLoginTime(Long userId) {
        baseMapper.update(null, new LambdaUpdateWrapper<SysUser>()
                .eq(SysUser::getId, userId)
                .set(SysUser::getLastLoginTime, new Date())
        );
    }
}




